package haxeframe.animation;

import flash.display.BitmapData;
import haxeframe.Globals;
import flash.display.Bitmap;
import flash.display.Sprite;
import openfl.display.Tilesheet;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.Lib;
 
class AnimatedSprite extends Sprite
{
    public var tilesheet:Tilesheet;
    public var tilesheetXml:Xml;

    private var lastAnimationTime:Int;
    private var _lastFrame:Int = -1;
    private var _currentFrame:Int = 0;
    public var totalFrames:Int = 0;
    public var currentSequenceFrame:Int = 0;

    public var totalSequenceFrames(get, never):Int;
    public function get_totalSequenceFrames():Int
    {
        if(currentSequenceArray == null) return 0;
        return currentSequenceArray.length;
    }

    private var frameOffset:Array<Array<Int>>;

    public var sourceBitmapData:BitmapData;

    private var sequences:Map<String, Array<Dynamic>>;
    public var currentSequence:String;
    private var currentSequenceArray:Array<Int>;

    private var loop:Bool;

    public var paused:Bool;

    public var fps:Int = 30;
    // public var mpf:Float = 1000/30;

    public var lastUpdate:Int;
    public var elapsed:Int;

    private var complete:Bool;
        
    public var frameWidth:Int = 0;
    public var frameHeight:Int = 0;

    public var ignoreScale:Bool;

    public var playOnAdded:Bool = true;
    public var stopOnRemoved:Bool = true;
 
    public function new (xml:Xml, bitmapData:BitmapData)
    {
        super();
        tilesheetXml = xml;
        sourceBitmapData = bitmapData;
        frameOffset = new Array<Array<Int>>();
        sequences = new Map<String, Array<Dynamic>>();
        addEventListener(Event.REMOVED_FROM_STAGE, onRemoved);
        addEventListener(Event.ADDED_TO_STAGE, onAdded);
        init();
    }
 
    public function init () : Void
    {
        if(sourceBitmapData == null || tilesheetXml == null) return;

        tilesheet = new Tilesheet(sourceBitmapData);
 		
        var frameNum:Int = 0;

        for(element in tilesheetXml.elements())
        {
        	tilesheet.addTileRect(new Rectangle(
        		Math.round(Std.parseInt(element.get('x'))),
        		Math.round(Std.parseInt(element.get('y'))),
        		Math.round(Std.parseInt(element.get('width'))),
        		Math.round(Std.parseInt(element.get('height')))
        	));

            if(Std.parseInt(element.get('frameWidth')) > frameWidth) frameWidth = Std.parseInt(element.get('frameWidth'));
            if(Std.parseInt(element.get('frameHeight')) > frameHeight) frameHeight = Std.parseInt(element.get('frameHeight'));

            frameOffset[frameNum] = [-Math.round(Std.parseInt(element.get('frameX'))), -Math.round(Std.parseInt(element.get('frameY')))];
        	++frameNum;
        }
 		totalFrames = frameNum;
    }

    private function onAdded(event:Event):Void
    {
        if(playOnAdded)
            play(loop);
    }

    private function onRemoved(event:Event):Void
    {
        if(stopOnRemoved)
            stop();
    }

    public var currentFrame(get,set):Int;
    public function get_currentFrame():Int
    {
    	if(currentSequenceArray != null){
    		for(f in 0...currentSequenceArray.length){
    			if(currentSequenceArray[f] == _currentFrame){
    				return f;
    			}
    		}
    		return 0;
		} else {
			return 0;
		}
    }

    public function set_currentFrame(value:Int):Int
    {
    	if(currentSequenceArray != null){
    		if(value < 0){
    			_currentFrame = 0;
    		} else if(value >= currentSequenceArray.length){
    			_currentFrame = currentSequenceArray.length-1;
    		} else {
    			_currentFrame = value;
    		}
		}
    	return _currentFrame;
    }

    public function addSequence(sequence:String, fps:Int=30):Void
    {
        if(tilesheetXml == null){
            trace('Cannot add sequence : '+sequence+' because tilesheetXml does not exist');
            return;
        }
    	var sequenceArray:Array<Int> = [];
    	var frameNum:Int = 0;
        for(element in tilesheetXml.elements())
        {
        	if(element.get('name').indexOf(sequence) > -1){
        		sequenceArray.push(frameNum);
        	}
        	++frameNum;
        }
        sequences.set(sequence, [fps, sequenceArray]);
    }
 
    public function playSequence(sequence:String, loopSequence:Bool=false):Void
    {
        if(tilesheet == null){
            trace('Cannot play sequence '+sequence+' because tilesheet does not exist');
            return;
        }
    	if(sequences.get(sequence) != null){
            Juggler.add(this);
            complete = false;
	    	currentSequence = sequence;
	    	fps = sequences.get(currentSequence)[0];
	    	currentSequenceArray = sequences.get(currentSequence)[1];
	    	currentSequenceFrame = 0;
	    	_currentFrame = currentSequenceArray[currentSequenceFrame];
	    	loop = loopSequence;
            paused = false;
    	}
    }

    public function stop():Void
    {
		paused = true;
        currentSequence = null;
        Juggler.remove(this);
    }

    public function play(doLoop:Bool=false):Void
    {
        // trace('play : '+this.name);
    	if(currentSequence == null){
    		for(sequence in sequences.keys()){
                currentSequence = sequence;
                break;
    		}
    	}
        playSequence(currentSequence, doLoop);
        Juggler.add(this);
    	paused = false;
    }

    public function gotoAndPlay(frameNum:Int, doLoop:Bool=false):Void
    {
        _currentFrame = currentSequenceArray[frameNum-1];
    	play(doLoop);
    }

    public function update (elapsed:Float) : Void 
    {
        if(currentSequence != null){

        	if(currentSequenceFrame+Math.round(elapsed*fps) < currentSequenceArray.length && !paused){
        		currentSequenceFrame+=Math.round(elapsed*fps);
    			_currentFrame = currentSequenceArray[currentSequenceFrame];
        	} else{
                currentSequenceFrame = currentSequenceArray.length-1;
                _currentFrame = currentSequenceArray[currentSequenceFrame];
                if(!complete){
                    dispatchEvent(new Event(Event.COMPLETE));
                    complete = true;
                }
        	}
        }
	 
        if(_lastFrame != _currentFrame){
    		graphics.clear();
            tilesheet.drawTiles(graphics, [frameOffset[_currentFrame][0]*Globals.SCALE_FACTOR, frameOffset[_currentFrame][1]*Globals.SCALE_FACTOR, _currentFrame, Globals.SCALE_FACTOR], true, Tilesheet.TILE_SCALE);
        }
        _lastFrame = _currentFrame;

        if(currentSequenceArray == null) return;

        if(currentSequenceFrame == currentSequenceArray.length-1){
            if(loop && !paused){
                currentSequenceFrame = -1;
                // _currentFrame = currentSequenceArray[currentSequenceFrame];
                complete = false;
            } else {
                paused = true;
            }
        }
	}
 
}